package br.edu.curso.java.dao;

import java.lang.reflect.Field;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;

import br.edu.curso.java.VO.BaseVO;


public abstract class BaseDAO<t extends BaseVO>  {
    
	public abstract String getChavePrimaria();
	public abstract String getNomeTabela();
	protected abstract t getObjeto();
	
    protected static Connection getConexao() {
		Connection conexao = null;
		try {
			Class.forName("org.postgresql.Driver");
			conexao = DriverManager
					.getConnection(
							"jdbc:postgresql://localhost:5432/AcademicNet",
							"postgres", "admin");

		} catch (Exception e) {
                                    e.printStackTrace();
		}
		return conexao;
	}
    
    private List<String> campos;
    
	public  void alterar(t obj) throws Exception{
		try {
			Connection conexao = getConexao();
			
			PreparedStatement pstm = conexao.prepareStatement(retornarComandoAlterar());
			
			pstm = carregarParametrosAlterar(pstm, obj);

			pstm.execute();
			pstm.close();
			conexao.close();
			
		} catch (Exception e) {
                        e.printStackTrace();
                        throw e;
		}
    }
	
    	protected abstract int carregarCampos(PreparedStatement pstm, t obj, int i);

    	protected abstract int carregarChavePrimaria(PreparedStatement pstm, t obj, int i);
    	
    	protected abstract t carregarObjeto(ResultSet rs);
    	
        protected PreparedStatement carregarParametrosAlterar(PreparedStatement pstm, t obj) { 
    		
    		int i = this.carregarCampos(pstm, obj, 1);
    		this.carregarChavePrimaria(pstm, obj, ++i);
    		
    		return pstm;
    	}
        
        protected PreparedStatement carregarParametrosExcluir(PreparedStatement pstm, t obj) { 
    		this.carregarChavePrimaria(pstm, obj, 1);
    		    		
    		return pstm;
    	}
        
        protected PreparedStatement carregarParametrosInserir(PreparedStatement pstm, t obj) { 
    		
        	//int i = this.carregarChavePrimaria(pstm, obj, 1);
    		
    		this.carregarCampos(pstm, obj, 1);
    		
    		return pstm;
    	}
        

		public  void inserir(t obj) throws Exception{
			try {
				
				Connection conexao = getConexao();
				
				PreparedStatement pstm = conexao.prepareStatement(retornarComandoInserir());
				
    				pstm = carregarParametrosInserir(pstm, obj);

				pstm.execute();
				pstm.close();
				conexao.close();
				
			} catch (Exception e) {
                        	e.printStackTrace();
                                throw e;
			}
		}

		public List<t> listar() {
						
						
			List<t> lista = new ArrayList<t>();
			
			try {
				Connection conexao = getConexao();
				Statement stm = conexao.createStatement();
				ResultSet rs = stm.executeQuery(retornarComandoListar());
				while (rs.next()) {
					lista.add(carregarObjeto(rs));
				}
				stm.close();
				conexao.close();
				
			} catch (Exception e) {
				e.printStackTrace();
			}
			return lista;
		}

		private  String retornarComandoAlterar() {
        	StringBuilder comando = new StringBuilder();
              	
           	comando.append("update  ");
        	comando.append(getNomeTabela());
        	comando.append(" set ");

        	Short i = 0;
        	
        	for (String campo : getCampos()) {
        		comando.append(campo);
        		comando.append("=?");
        		i++;
				if (i < getCampos().size()) {
					comando.append(", ");
				}
			}
        	
        	comando.append(" where ");
        	        	
        	i = 0;
        	

        		comando.append(getChavePrimaria());
        		comando.append("= ?");


        	comando.append("; ");
   
        	return comando.toString();
        }

        private  String retornarComandoInserir() {
        	
        	StringBuilder comando = new StringBuilder();
        	StringBuilder nomesCampos = new StringBuilder();
        	StringBuilder parametros = new StringBuilder();
        	
        	comando.append("Insert into ");
        	comando.append(getNomeTabela());
        	comando.append(" (");

        	//	nomesCampos.append(getChavePrimaria());
        	//	nomesCampos.append(", ");
        	//	parametros.append("?, ");

        	Short i = 0;
        	
        	for (String campo : getCampos()) {
        		nomesCampos.append(campo);
        		parametros.append("? ");
        		i++;
				if (i < getCampos().size()) {
					nomesCampos.append(", ");
					parametros.append(", ");
				}
			}
        	
        	comando.append(nomesCampos.toString());
        	comando.append(") values (");
        	comando.append(parametros.toString());
        	
        	comando.append("); ");
   
        	System.out.println(comando.toString());
        	
        	return comando.toString();
        	
           }

        private  String retornarComandoListar() {
        	
        	StringBuilder comando = new StringBuilder();
        	
           	comando.append("select * from ");
        	
        	comando.append(getNomeTabela());
       	
        	comando.append("; ");
   
        	return comando.toString();
        	
        }
        
     
        protected void setCampos(List<String> campos) {
	                                                
			this.campos = campos;
		}
        
        protected String getNomesDosCampos() {
        	
    		StringBuilder listaDeCampoos = new StringBuilder();
    		String ultimoCampo = getCampos().get(getCampos().size() - 1);
    		
        	for (String nomeCampo : getCampos()) {
				listaDeCampoos.append(nomeCampo);
				if (! nomeCampo.equalsIgnoreCase(ultimoCampo))  {
					listaDeCampoos.append(",");					
				}
			}
        	
        	return listaDeCampoos.toString();
        	        	
        }
        
        protected List<String> getCampos() {
          	
        	if (campos == null) {
				  
        		campos = new ArrayList<String>();

        			// Fiz isso pois n�o consegui instanciar o m�todo por reflection
		            //Field f[] = getObjeto().getClass().getDeclaredFields();
		            
                        Field camposHerdados[] = getObjeto().getClass().getSuperclass().getDeclaredFields();
		                                       
		            for (int j = 0; j < camposHerdados.length; j++) {  
		              	if (! camposHerdados[j].getName().equalsIgnoreCase(this.getChavePrimaria())) {
		            		campos.add(camposHerdados[j].getName());
		            	}
		            }
							
                        Field f[] = getObjeto().getClass().getDeclaredFields();
		                                       
		            for (int j = 0; j < f.length; j++) {  
		              	if (! f[j].getName().equalsIgnoreCase(this.getChavePrimaria())) {
		            		campos.add(f[j].getName());
		            	}
		            }
        	}
                

                return campos;
		}

}
